//=============================================================================
#include "CNetwork.h"

//=============================================================================
CNetwork* CNetwork::Instance = NULL;

//=============================================================================
CNetwork::CNetwork() {
	Connected = false;

	if(Instance == NULL) {
		Instance = this;
	}
}

//-----------------------------------------------------------------------------
void CNetwork::OnUser(Uint8 type, int code, void* data1, void* data2) {
    if(IsConnected() == false) return;

    //data1 = Socket
    switch(code) {
        case NET2_TCPACCEPTEVENT: {
            Client.Connect((int)data1);
            break;
        }

        case NET2_TCPRECEIVEEVENT: {
            CNetworkPacket* Packet = Client.GetReadPacket();

            int Return = 0;

            do {
                if(Packet->NeedsHeader())   Return = Packet->ReadHeader(Client.GetSocket());
                else                        Return = Packet->ReadData(Client.GetSocket());

                if(Packet->IsReady())       Packet = Client.GetReadPacket();
            }while(Return > 0);

            break;
        }

        case NET2_TCPCLOSEEVENT: {
            Disconnect();
            break;
        }

        case NET2_ERROREVENT: {
            break;
        }
    }
}

//-----------------------------------------------------------------------------
bool CNetwork::OnInit() {
    if(SDLNet_Init() < 0) {
        CLog::Error("Unable to initialize SDL_net : " + cStringify(SDL_GetError()));
        return false;
    }

    if(NET2_Init() == -1) {
        CLog::Error("Unable to initialize SDL_net : " + cStringify(NET2_GetError()));
        return false;
    }

	CLog::Notice("SDL_net Initialized");

	return true;
}

//-----------------------------------------------------------------------------
void CNetwork::OnLoop() {
    if(IsConnected() == false) return;

    switch(Client.GetMode()) {
        case CNETWORK_MODE_LOGIN: {
            Client.PlayerID = -1;

            srand(time(NULL));

            std::string UID = "0123000011112222333344443289" + Stringify(rand() % 10) + Stringify(rand() % 10) + Stringify(rand() % 10); //32 length UID

            CNetworkStream::SendHeader(Network->GetSocket(), CNETWORK_MESSAGE_LOGIN, UID.size());
            CNetworkStream::SendString(Network->GetSocket(), UID);

            Client.SetMode(CNETWORK_MODE_LOGIN_WAIT);

            printf("Send UID : %s\n", UID.c_str());

            break;
        }

        case CCLIENTS_MODE_CONFIRMFILELIST: {
            printf("Confirming Files\n");

            char CWD[512];
            getcwd(CWD, 512);

            std::string CachePath = cStringify(CWD) + "/Cache/KizareWorld/";

            CreateDirectory("./Cache/", 0);
            CreateDirectory("./Cache/KizareWorld/", 0);

            chdir(CachePath.c_str());

            for(int i = 0;i < FileList.size();i++) {
                CClientsFiles* File = &FileList[i];

                //Is Folder
                if(File->IsFolder) {
                    File->State = CCLIENTSFILES_STATE_IGNORE;

                    std::vector<std::string> PathParts = StringifyExplode(File->Filename, "/");
                    std::string Path      = ".";

                    //0 is .
                    for(int i = 1;i < PathParts.size();i++) {
                        Path = Path + "/" + PathParts[i];

                        CreateDirectory(Path.c_str(), 0);
                    }
                }else{
                    File->State = CCLIENTSFILES_STATE_DOWNLOAD;

                    FILE* FileHandle = fopen(File->Filename.c_str(), "w");
                    fclose(FileHandle);
                }

                std::string Packet = Stringify(i) + " " + (File->State == CCLIENTSFILES_STATE_DOWNLOAD ? "1" : "0");

                CNetworkStream::SendHeader(Network->GetSocket(), CNETWORK_MESSAGE_FILELIST_CONFIRM, Packet.size());
                CNetworkStream::SendString(Network->GetSocket(), Packet);
            }

            chdir(CWD);

            Client.SetMode(CNETWORK_MODE_GETFILES);
            break;
        }
    }

    for(unsigned int i = 0;i < Client.Packets.size();i++) {
        CNetworkPacket* Packet = &Client.Packets[i];

        if(Packet->IsReady() == false) continue;

        switch(Client.GetMode()) {
            case CNETWORK_MODE_LOGIN_WAIT: {
                if(Packet->GetMessageID() == CNETWORK_MESSAGE_LOGIN_OK) {
                    printf("Logged in...");
                    Client.SetMode(CNETWORK_MODE_GETFILELIST);
                }else{
                    printf("Bad Login");
                    Client.SetMode(CNETWORK_MODE_LOGIN);
                }

                FileList.clear();

                break;
            }

            case CNETWORK_MODE_GETFILELIST: {
                if(Packet->GetMessageID() == CNETWORK_MESSAGE_FILELIST) {
                    std::vector<std::string> Parts = StringifyExplode(Packet->GetDataString(), " ");

                    CClientsFiles TempFile;

                    TempFile.Filename   = Parts[0];
                    TempFile.BytesSent  = atoi(Parts[1].c_str());
                    TempFile.Checksum   = Parts[2];
                    TempFile.IsFolder   = (Parts[3] == "1");

                    FileList.push_back(TempFile);
                }else
                if(Packet->GetMessageID() == CNETWORK_MESSAGE_FILELIST_DONE) {
                    Client.SetMode(CCLIENTS_MODE_CONFIRMFILELIST); //Actually download them
                }

                break;
            }

            case CNETWORK_MODE_GETFILES: {
                std::string Filename    = "";
                Uint32 TotalBytes       = 0;
                std::string Checksum    = "";

                char CWD[512];
                getcwd(CWD, 512);

                std::string CachePath = cStringify(CWD) + "/Cache/KizareWorld/";

                chdir(CachePath.c_str());

                for(int i = FileList.size() - 1;i >= 0;i--) {
                    CClientsFiles* File = &FileList[i];

                    if(File->State == CCLIENTSFILES_STATE_IGNORE) {
                        FileList.pop_back();
                        continue;
                    }

                    if(Packet->GetMessageID() == CNETWORK_MESSAGE_FILE) {
                        FILE* FileHandle = fopen(File->Filename.c_str(), "ab");

                        fwrite(Packet->GetData(), 1, Packet->GetDataSize(), FileHandle);

                        fclose(FileHandle);
                    }else
                    if(Packet->GetMessageID() == CNETWORK_MESSAGE_FILE_DONE) {
                        printf("File : %s - %d\n", File->Filename.c_str(), i);

                        FileList.pop_back();
                    }

                    break;
                }

                if(FileList.size() == 0) {
                    chdir(CWD);

                    printf("Done\n");

                    Client.SetMode(CCLIENTS_MODE_ENTER_WORLD);
                }

                break;
            }

            case CCLIENTS_MODE_ENTER_WORLD: {
                if(Packet->GetMessageID() == CNETWORK_MESSAGE_ENTER_WORLD) {
                    std::string PlayerIDString = Packet->GetDataString();

                    Network->PlayerID = atoi(PlayerIDString.c_str());

                    printf("%s\n", PlayerIDString.c_str());

                    Client.SetMode(CNETWORK_MODE_UPDATE);
                }

                break;
            }

            case CNETWORK_MODE_UPDATE: {
                if(Packet->GetMessageID() == CNETWORK_MESSAGE_UPDATE_PLAYER) {
                    std::vector<std::string> Parts = StringifyExplode(Packet->GetDataString(), " ");

                    std::string PlayerIDString = Parts[0];

                    int ThisPlayerID = atoi(PlayerIDString.c_str());

                    CPlayer::PlayerList[ThisPlayerID].Unserialize(Packet->GetDataString());
                }

                break;
            }

            default: {
                break;
            }
        }
    }
}

//-----------------------------------------------------------------------------
void CNetwork::OnCleanup() {
	Disconnect();

    NET2_Quit();
    SDLNet_Quit();
}

//-----------------------------------------------------------------------------
bool CNetwork::Connect(char* IP) {
    //std::string PortString = CSystem::GetConfigValue("Port");
    int Port = 22012; //atoi(PortString.c_str());

    CLog::Notice("Connect to " + cStringify(IP) + " on Port " + Stringify(Port));

    if(NET2_TCPConnectTo(IP, Port) == -1) {
       CLog::Notice("Unable to Connect to Host : " + cStringify(SDL_GetError()));
       return false;
    }

    CLog::Notice("Connected...");

	return true;
}

//-----------------------------------------------------------------------------
void CNetwork::Disconnect() {
    Client.Disconnect();
}

//-----------------------------------------------------------------------------
bool CNetwork::IsConnected() {
	return !Client.IsAvailable();
}

//=============================================================================
CNetwork* CNetwork::GetInstance() {
	return CNetwork::Instance;
}

//=============================================================================
